package org.epidroid.epidroid.chronos.engine.parsers;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.epidroid.epidroid.chronos.engine.Chronos;
import org.epidroid.epidroid.chronos.engine.exceptions.ChronosException;
import org.epidroid.epidroid.chronos.engine.objects.ChronosCalendar;
import org.epidroid.epidroid.chronos.engine.objects.ChronosCourse;
import org.epidroid.epidroid.chronos.engine.objects.ChronosDay;
import org.epidroid.epidroid.chronos.engine.objects.ChronosInstructor;
import org.epidroid.epidroid.chronos.engine.objects.ChronosRoom;
import org.epidroid.epidroid.chronos.engine.objects.ChronosTrainee;
import org.epidroid.epidroid.chronos.engine.objects.ChronosWeek;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import android.util.Log;

public class CalendarParser {

	public static ChronosCalendar parse(String input) throws ChronosException {

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = null;
		try {
			builder = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			Log.e(Chronos.LOGCAT, e.getMessage());
			throw new ChronosException("Could not parse response");
		}
		Document dom = null;

		InputSource inStream = new InputSource();
		inStream.setCharacterStream(new StringReader(input));
		try {
			dom = builder.parse(inStream);
		} catch (SAXException e) {
			Log.e(Chronos.LOGCAT, e.getMessage());
			throw new ChronosException("Could not parse response");
		} catch (IOException e) {
			Log.e(Chronos.LOGCAT, e.getMessage());
			throw new ChronosException("Could not parse response");
		}

		ChronosCalendar calendar = new ChronosCalendar();
		Element root = dom.getDocumentElement();
		parseWeeks(calendar, root);

		return calendar;
	}

	public static void parseWeeks(ChronosCalendar calendar, Element root) {
		NodeList list = root.getChildNodes();

		for (int i = 0; i < list.getLength(); i++) {
			Node item = list.item(i);
			if (!item.getNodeName().equals("week")) {
				continue;
			}
			ChronosWeek week;
			try {
				week = parseWeek(item);
				if (null != week)
					calendar.addWeek(week);
			} catch (ChronosException e) {
				// Nothing to do here
			}
		}
	}

	public static ChronosWeek parseWeek(Node node) throws ChronosException {
		List<ChronosDay> days = new ArrayList<ChronosDay>();
		String id = null;

		NodeList list = node.getChildNodes();
		for (int i = 0; i < list.getLength(); i++) {
			Node item = list.item(i);
			if (item.getNodeName().equals("id") && (item instanceof Element)) {
				id = ((Element) item).getTextContent();
			} else if (item.getNodeName().equals("day")) {
				ChronosDay day = parseDay(item);
				if (null != day)
					days.add(day);
			}
		}

		if (id == null) {
			Log.e(Chronos.LOGCAT, "There is no id for this week");
			throw new ChronosException("Could not retrieve week");
		}
		ChronosWeek res = new ChronosWeek(id);
		res.setDays(days);
		return res;
	}

	public static ChronosDay parseDay(Node node) throws ChronosException {
		List<ChronosCourse> courses = new ArrayList<ChronosCourse>();
		String id = null;
		String date = null;

		NodeList list = node.getChildNodes();
		for (int i = 0; i < list.getLength(); i++) {
			Node item = list.item(i);
			if (item.getNodeName().equals("id") && (item instanceof Element)) {
				id = ((Element) item).getTextContent();
			} else if (item.getNodeName().equals("date")
					&& (item instanceof Element)) {
				date = ((Element) item).getTextContent();
			} else if (item.getNodeName().equals("course")) {
				ChronosCourse course = parseCourse(item);
				if (null != course)
					courses.add(course);
			}
		}

		if (id == null) {
			Log.e(Chronos.LOGCAT, "There is no id for this day");
			throw new ChronosException("Could not retrieve the day");
		} else if (date == null) {
			Log.e(Chronos.LOGCAT, "There is no date for this day");
			throw new ChronosException("Could not retrieve the day");
		}

		ChronosDay res = new ChronosDay(id, date);
		res.setCourses(courses);
		return res;
	}

	public static ChronosCourse parseCourse(Node node) {
		List<ChronosInstructor> instructors = new ArrayList<ChronosInstructor>();
		List<ChronosTrainee> trainees = new ArrayList<ChronosTrainee>();
		List<ChronosRoom> rooms = new ArrayList<ChronosRoom>();
		String id = null;
		String title = null;
		String hour = null;
		String duration = null;

		NodeList list = node.getChildNodes();
		for (int i = 0; i < list.getLength(); i++) {
			Node item = list.item(i);
			if (item.getNodeName().equals("id") && (item instanceof Element)) {
				id = ((Element) item).getTextContent();
			} else if (item.getNodeName().equals("title")
					&& (item instanceof Element)) {
				title = ((Element) item).getTextContent();
			} else if (item.getNodeName().equals("hour")
					&& (item instanceof Element)) {
				hour = ((Element) item).getTextContent();
			} else if (item.getNodeName().equals("duration")
					&& (item instanceof Element)) {
				duration = ((Element) item).getTextContent();
			} else if (item.getNodeName().equals("instructor")
					&& (item instanceof Element)) {
				ChronosInstructor instructor = new ChronosInstructor(
						((Element) item).getTextContent());
				if (null != instructor)
					instructors.add(instructor);
			} else if (item.getNodeName().equals("room")
					&& (item instanceof Element)) {
				ChronosRoom room = new ChronosRoom(
						((Element) item).getTextContent());
				if (null != room)
					rooms.add(room);
			} else if (item.getNodeName().equals("trainee")
					&& (item instanceof Element)) {
				ChronosTrainee trainee = new ChronosTrainee(
						((Element) item).getTextContent());
				if (null != trainee)
					trainees.add(trainee);
			}
		}

		if (id == null) {
			Log.e(Chronos.LOGCAT, "There is no id for this course");
			return null;
		} else if (title == null) {
			Log.e(Chronos.LOGCAT, "There is no date for this course");
			return null;
		} else if (hour == null) {
			Log.e(Chronos.LOGCAT, "There is no hour for this course");
			return null;
		} else if (duration == null) {
			Log.e(Chronos.LOGCAT, "There is no duration for this course");
			return null;
		}

		ChronosCourse res = new ChronosCourse(id, title, hour, duration);
		res.setInstructors(instructors);
		res.setTraiees(trainees);
		res.setRooms(rooms);
		return res;
	}
}
